package main

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"gitee.com/h2ob/api"
	"gitee.com/h2ob/base"
	"gitee.com/h2ob/infra"
	"github.com/gin-gonic/gin"
)

func statTrace() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		c.Next()
		latency := time.Since(t)
		status := c.Writer.Status()
		base.GLogger.Infof("perf stat: url=[%s], time used=[%d], status=[%d]", c.FullPath(), latency.Milliseconds(), status)
	}
}

func verifySign(c *gin.Context) bool {
	sign := c.Request.Header.Get("X-H2o-SIGNATURE")
	noise := c.Request.Header.Get("X-H2o-NOISE")
	timestamp := c.Request.Header.Get("X-H2o-TIME")
	if (sign == "") || (noise == "") || (timestamp == "") {
		return false
	}
	reqTime, _ := strconv.ParseInt(timestamp, 10, 64)
	if math.Abs(float64(time.Now().UnixMilli()-reqTime))/1000 >= 10 {
		base.GLogger.Infof("Request is too old")
		c.String(http.StatusBadRequest, "Request is too old")
		c.Abort()
		return true
	}

	nsign := base.MD5Sign(timestamp, noise, base.GConf.Http.Sault)
	if nsign != sign {
		base.GLogger.Infof("sign(%s) != nsign(%s)", sign, nsign)
		c.String(http.StatusUnauthorized, "Authorization empty")
		c.Abort()
		return true
	}
	c.Next()
	return true
}

func jwtAuthMiddleware(c *gin.Context) {
	if verifySign(c) {
		return
	}
	authHeader := c.Request.Header.Get("Authorization")
	if authHeader == "" {
		base.GLogger.Infof("Authorization empty")
		c.String(http.StatusUnauthorized, "Authorization empty")
		c.Abort()
		return
	}
	parts := strings.SplitN(authHeader, " ", 2)
	if (len(parts) != 2) || (parts[0] != "Bearer") {
		base.GLogger.Infof("Authorization error")
		c.String(http.StatusUnauthorized, "Authorization error")
		c.Abort()
		return
	}

	err := base.VerifyToken(parts[1], c)
	if err != nil {
		base.GLogger.Infof("Authorization faild")
		c.String(http.StatusUnauthorized, "Authorization faild")
		c.Abort()
		return
	}
	c.Next()
}

func startNeed(conf string, ctx *context.Context) {
	base.GHBase.Init(conf)
	infra.InfraStart(ctx)
}

func stopNeed() {
	infra.InfraStop()
}

// @title H20
// @version 1.0
// @description Go
// @termsOfService https://gitee.com/yedao2090/h2-ob/wikis
func main() {
	conf := "../config/h2o.yaml"
	args := os.Args
	if len(args) >= 2 {
		conf = args[1]
	}
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()
	startNeed(conf, &ctx)
	base.GLogger.Info("H2o Go")
	router := gin.New()
	router.MaxMultipartMemory = 8 << 20
	router.Use(statTrace())
	router.Use(gin.Recovery())
	api.RegisterApi(base.GConf.Http.Prefix, router, jwtAuthMiddleware)

	srv := &http.Server{
		Addr:    fmt.Sprintf(":%d", base.GConf.Http.Port),
		Handler: router,
	}

	// Initializing the server
	go func() {
		/**进行连接**/
		base.GLogger.Infof("server listen on:%d", base.GConf.Http.Port)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			base.GLogger.Errorf("listen error:%s", err.Error())
		}
	}()
	// wait
	<-ctx.Done()
	stop()
	base.GLogger.Info("Shutdown Server ...")
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctx); err != nil {
		base.GLogger.Warn("Server shutdown:", err)
	}
	stopNeed()
	base.GLogger.Info("Server exist")
}
